#include <GL/gl.h>
#include <iostream>

#include <vector>
#include <algorithm>
#include "cuboidPrimitive.h"
#include "defines/primitive.h"
#include "defines/axes.h"
#include "defines/octreeType.h"

CuboidPrimitive::CuboidPrimitive(int _id) : Object(_id){
    createPoints();
}

CuboidPrimitive::CuboidPrimitive(const Vector3D &_min, const Vector3D &_max, int _id) : Object(_id, CUBOID, true){
    createPoints();
    setAllPoints(_min, _max);
}

CuboidPrimitive::CuboidPrimitive(const Vector3D &_min, const Vector3D &_max, const Color &_c, int _id) : Object(_c, _id, CUBOID, true){
    createPoints();
    setAllPoints(_min, _max);
}

CuboidPrimitive::~CuboidPrimitive(){
	delete [] point;
}

void CuboidPrimitive::setAllPoints(const Vector3D & _min, const Vector3D &_max){
    setBoxPoints(_min, _max, point);
//    point[0] = _min;
//    point[1] = Vector3D(_max.x, _min.y, _min.z);
//    point[2] = Vector3D(_min.x, _min.y, _max.z);
//    point[3] = Vector3D(_max.x, _min.y, _max.z);
//    point[4] = Vector3D(_min.x, _max.y, _min.z);
//    point[5] = Vector3D(_max.x, _max.y, _min.z);
//    point[6] = Vector3D(_min.x, _max.y, _max.z);
//    point[7] = _max;
}

Vector3D CuboidPrimitive::getPoint(unsigned int _i) const{
    if(_i > 7){
        std::cout << "Tentando pegar um ponto que não existe no hexaedro.";
        exit(1);
    }
    return point[_i];
}

double CuboidPrimitive::volume() const{
    return (point[7].x - point[0].x)*(point[7].y - point[0].y)*(point[7].z - point[0].z);
}

double CuboidPrimitive::area() const{
    double inX = point[7].x - point[0].x;
    double inY = point[7].y - point[0].y;
    double inZ = point[7].z - point[0].z;
    return 2*((inX * inY) + (inX * inZ) + (inY * inZ));
}

void CuboidPrimitive::draw(bool _wireframe, bool _useParentColor, const Color &_parentColor) const{
//    if(_wireframe){
//        glDisable(GL_LIGHT0);
//        glDisable(GL_LIGHTING);
//
//        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//        glDisable(GL_CULL_FACE);
//
//        glLineWidth(2);
//    }
//
//    Vector3D normal;
//    glBegin(GL_QUADS);
//        Color color_;
//        if(_useParentColor) color_ = _parentColor;
//        else color_ = color;
//
//        if(_wireframe && selected){
//            if(color_ != Color(1,0,0,1)) color_ = Color(1, 0, 0, 1);
//            else color_ = Color(1, 1, 1, 1);
//        }
//
//        glColor4d(color_.red, color_.green, color_.blue, color_.alpha);
//
//    //parte de baixo
//        normal = ((point[4] - point[0]).crossProduct(point[1] - point[0])).normalize();
//        glNormal3f(normal.x, normal.y, normal.z);
//        glVertex3f(point[0].x, point[0].y, point[0].z);
//        glVertex3f(point[4].x, point[4].y, point[4].z);
//        glVertex3f(point[5].x, point[5].y, point[5].z);
//        glVertex3f(point[1].x, point[1].y, point[1].z);
//
//
//    //parte de cima
//        glNormal3f(-normal.x, -normal.y, -normal.z);
//        glVertex3f(point[2].x, point[2].y, point[2].z);
//        glVertex3f(point[3].x, point[3].y, point[3].z);
//        glVertex3f(point[7].x, point[7].y, point[7].z);
//        glVertex3f(point[6].x, point[6].y, point[6].z);
//
//    //parte de esquerda
//        normal = ((point[2] - point[0]).crossProduct(point[4] - point[0])).normalize();
//        glNormal3f(normal.x, normal.y, normal.z);
//        glVertex3f(point[0].x, point[0].y, point[0].z);
//        glVertex3f(point[2].x, point[2].y, point[2].z);
//        glVertex3f(point[6].x, point[6].y, point[6].z);
//        glVertex3f(point[4].x, point[4].y, point[4].z);
//
//    //parte de direita
//        glNormal3f(-normal.x, -normal.y, -normal.z);
//        glVertex3f(point[1].x, point[1].y, point[1].z);
//        glVertex3f(point[5].x, point[5].y, point[5].z);
//        glVertex3f(point[7].x, point[7].y, point[7].z);
//        glVertex3f(point[3].x, point[3].y, point[3].z);
//
//    //parte de frente
//        normal = ((point[1] - point[0]).crossProduct(point[2] - point[0])).normalize();
//        glNormal3f(normal.x, normal.y, normal.z);
//        glVertex3f(point[0].x, point[0].y, point[0].z);
//        glVertex3f(point[1].x, point[1].y, point[1].z);
//        glVertex3f(point[3].x, point[3].y, point[3].z);
//        glVertex3f(point[2].x, point[2].y, point[2].z);
//
//    //parte de tras
//        glNormal3f(-normal.x, -normal.y, -normal.z);
//        glVertex3f(point[4].x, point[4].y, point[4].z);
//        glVertex3f(point[6].x, point[6].y, point[6].z);
//        glVertex3f(point[7].x, point[7].y, point[7].z);
//        glVertex3f(point[5].x, point[5].y, point[5].z);
//    glEnd();
//
//
//    if(_wireframe){
//        glLineWidth(1);
//
//        glEnable(GL_LIGHT0);
//        glEnable(GL_LIGHTING);
//
//        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//        glEnable(GL_CULL_FACE);
//    }else{
//        if(selected) draw(true);
//    }



    Vector3D min, max;
    getMinMax(&min.x, &max.x, X_AXIS);
    getMinMax(&min.y, &max.y, Y_AXIS);
    getMinMax(&min.z, &max.z, Z_AXIS);

    drawBox(_wireframe, min, max, _useParentColor, _parentColor);

    if(!_wireframe && selected) drawBox(true, min, max);
}

int CuboidPrimitive::classify(Vector3D *_octreePoint) const{
    //pegnado logo os casos WHITE
    Vector3D minCuboid = point[0];
    Vector3D maxCuboid = point[7];
    Vector3D minOctree = _octreePoint[0];
    Vector3D maxOctree = _octreePoint[7];

    if( minCuboid.x >= maxOctree.x ) return WHITE;
    if( maxCuboid.x <= minOctree.x ) return WHITE;
    if( minCuboid.y >= maxOctree.y ) return WHITE;
    if( maxCuboid.y <= minOctree.y ) return WHITE;
    if( minCuboid.z >= maxOctree.z ) return WHITE;
    if( maxCuboid.z <= minOctree.z ) return WHITE;

    if( (minOctree.x >= minCuboid.x) && (minOctree.x <= maxCuboid.x) )
        if( (maxOctree.x >= minCuboid.x) && (maxOctree.x <= maxCuboid.x) )
            if( (minOctree.y >= minCuboid.y) && (minOctree.y <= maxCuboid.y) )
                if( (maxOctree.y >= minCuboid.y) && (maxOctree.y <= maxCuboid.y) )
                    if( (minOctree.z >= minCuboid.z) && (minOctree.z <= maxCuboid.z) )
                        if( (maxOctree.z >= minCuboid.z) && (maxOctree.z <= maxCuboid.z) )
                            return BLACK;

    return GREY;
}

void CuboidPrimitive::createPoints(){
	point = new Vector3D[8];

	for(unsigned int i = 0; i < 8; i++)
		point[i] = Vector3D();
}
/*
         p6------p7 max
         /|      /|
        p2------P3|
        |P4-----|-p5
        |/      |/
    min p0------p1
    z
    |  y
    |/_ _ x

*/


void CuboidPrimitive::getMinMax(double *_min, double *_max, unsigned int _axis) const{
    switch(_axis){
        case X_AXIS:{
            std::vector<double> xCoordinates;
            for(int i = 0; i < 8; i++){
                xCoordinates.push_back(point[i].x);
            }
            sort(xCoordinates.begin(), xCoordinates.end());
            *_min = xCoordinates.at(0);
            *_max = xCoordinates.at(7);
            break;
        }
        case Y_AXIS:{
            std::vector<double> yCoordinates;
            for(int i = 0; i < 8; i++){
                yCoordinates.push_back(point[i].y);
            }
            sort(yCoordinates.begin(), yCoordinates.end());
            *_min = yCoordinates.at(0);
            *_max = yCoordinates.at(7);
            break;
        }
        case Z_AXIS:{
            std::vector<double> zCoordinates;
            for(int i = 0; i < 8; i++){
                zCoordinates.push_back(point[i].z);
            }

            sort(zCoordinates.begin(), zCoordinates.end());
            *_min = zCoordinates.at(0);
            *_max = zCoordinates.at(7);
            break;
        }
    }
}
